package ch.uzh.ifi.sonar.analyzers;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sourceforge.clearcase.commandline.CleartoolCommandLine;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PlatformUI;

import ch.uzh.ifi.sonar.importers.ClearCaseCommand;
import ch.uzh.ifi.sonar.qacpp.MetFileAnalyzer;
import ch.uzh.ifi.sonar.qacpp.QacppStarter;
import ch.uzh.ifi.sonar.util.SonarConstant;

/* *
 * Analyze the ClearCase Request and get the set of metrics
 * 
 * */
public class CRAnalyzer {

	private final String crNumber;
	/* TODO create a clearcase view first, currently it must mark a drive with clearcase view context
	 * 
	 * */
	private final static String DRIVE = "Z:";

	private static final String CSV_SEP = SonarConstant.CSV_SEP;
	private final File ticketDir;
	private final QacppStarter starter;

	private long sttlnSum = 0;
	private long stvarSum = 0;
	private long stoptSum = 0;
	private long stccbSum = 0;

	private List<String> changeSetVersions;
	private Date crDate;

	public CRAnalyzer(final String crNumber) {
		this.crNumber = crNumber;
		this.starter = new QacppStarter();
		this.ticketDir = new File(starter.getOutputLocation()
				+ File.separatorChar + crNumber);
		this.calculateCRDetails();
	}

	/*
	 * create a temporary source file and start qacpp.exe to analyze it,
	 * generate the met file
	 */
	public void launchAnalysis(String contentPath) throws IOException {
		// analyze contentPath and get the fileName first
		String fileNamePath = contentPath.substring(0,
				contentPath.indexOf("@@"));
		String fileName = fileNamePath
				.substring(fileNamePath.lastIndexOf("\\") + 1);
		if (fileName.endsWith(".h") || fileName.endsWith(".cpp")
				|| fileName.endsWith(".c")) {
			String tempFilePath = ticketDir.getAbsolutePath()
					+ File.separatorChar + fileName;
			if (this.hasTicketDir()) {
				File tempFile = this.createTempSrcFile(contentPath,
						tempFilePath);
				// File mFile = new File(tempFile.getAbsolutePath() + ".met");
				try {
					starter.executeProcess(tempFile.getAbsolutePath(),
							ticketDir.getAbsolutePath());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				MessageDialog.open(MessageDialog.ERROR, PlatformUI
						.getWorkbench().getDisplay().getActiveShell(),
						"CR Analyzer Error",
						"CRAnalyzer Error: creating a folder in \'" + ticketDir
								+ "\' fails !\n check your settings.via file!",
						SWT.NONE);
				System.out.println("CRAnalyzer Error: creating a folder in \'"
						+ ticketDir
						+ "\' fails !\n check your settings.via file!");
			}
		}
	}

	private void analyze() {
		java.util.List<String> rss = this.getChangeSetVersions();
		for (String str : rss) {
			try {
				this.launchAnalysis(str);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private void parseMetFiles() {
		File[] metFiles = ticketDir.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File file, String name) {
				return name.endsWith(".met");
			}
		});
		if (metFiles != null && metFiles.length != 0) {
			synchronized (this) {
				sttlnSum = stvarSum = stoptSum = stccbSum = 0;
				MetFileAnalyzer ma = null;
				for (File metFile : metFiles) {
					ma = new MetFileAnalyzer(metFile);
					System.out.println(ma.getSourceFileName() + ": " + ma.getSTTLN());
					sttlnSum += ma.getSTTLN();
					stvarSum += ma.getSTVAR();
					stoptSum += ma.getSTOPT();
					stccbSum += ma.getSTCCB();
				}
			}
		}
	}

	private void writeResultToSummaryFile(File summaryFile) {
	}

	public String crResultToString() {
		if (this.getCRDate() != null) {
			this.analyze();
			this.parseMetFiles();
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
			return this.crNumber + " [" + format.format(this.getCRDate())
					+ "]\n  Complexity Metrics: ["
					+ this.changeSetVersions.size() + " files] \n [STTLN: "
					+ Long.toString(this.sttlnSum) + " STVAR: "
					+ Long.toString(this.stvarSum) + " STOPT:"
					+ Long.toString(this.stoptSum) + " STCCB: "
					+ Long.toString(this.stccbSum) + "]";
		} else {
			
			return "";
		}

	}

	public String csvLine() {
		if (this.getCRDate() != null) {
			this.analyze();
			this.parseMetFiles();
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
			return this.crNumber + CSV_SEP + format.format(this.getCRDate())
					+ CSV_SEP + this.changeSetVersions.size() + CSV_SEP
					+ Long.toString(this.sttlnSum) + CSV_SEP
					+ Long.toString(this.stvarSum) + CSV_SEP
					+ Long.toString(this.stoptSum) + CSV_SEP
					+ Long.toString(this.stccbSum)
					+ System.getProperty("line.separator");
		} else {
			return "";
		}
	}
	
	public TableItem getTicketMetricItem(Table table, int style){
		TableItem item = new TableItem(table, style);
		this.analyze();
		this.parseMetFiles();
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		item.setText(0, this.crNumber);
		item.setText(1, format.format(this.getCRDate()));
		item.setText(2, Integer.toString(this.changeSetVersions.size()));
		item.setText(3, Long.toString(this.sttlnSum));
		item.setText(4, Long.toString(this.stvarSum));
		item.setText(5, Long.toString(this.stoptSum));
		item.setText(6, Long.toString(this.stccbSum));
		return item;
	}

	
	private File createTempSrcFile(String contentPath, String tempFilePath)
			throws IOException {
		BufferedReader srcReader = null;
		FileWriter writer = null;

		srcReader = new BufferedReader(new InputStreamReader(
				new FileInputStream(DRIVE + contentPath)));
		writer = new FileWriter(tempFilePath);
		int c;
		while ((c = srcReader.read()) != -1) {
			writer.write(c);
		}
		if (writer != null)
			writer.close();
		if (srcReader != null)
			srcReader.close();

		return new File(tempFilePath);
	}

	private boolean hasTicketDir() {
		return ticketDir.mkdir()
				| (ticketDir.exists() && ticketDir.isDirectory());
	}

	private String[] getCleartoolResult() {
		CleartoolCommandLine ct = new CleartoolCommandLine("lsact");
		ct.addElement("-l");
		ct.addElement(this.crNumber);
		String[] output = null;
		String[] errorOutput = null;
		Map<String, String[]> cmdResult = new ClearCaseCommand(ct).execute();
		output = cmdResult.get(ClearCaseCommand.SUCCEED);
		errorOutput = cmdResult.get(ClearCaseCommand.FAIL);
		if (errorOutput == null) {
			return output;
		} else {
			return errorOutput;
		}
	}

	public void calculateCRDetails() {
		String[] execResult = this.getCleartoolResult();
		for (String execLine : execResult) {
			if (execLine.contains("cleartool: Error")) {
				this.crDate = null;
				this.changeSetVersions = new ArrayList<String>();
				break;
			}
		}
		this.changeSetVersions = Collections
				.synchronizedList(new ArrayList<String>());

		Pattern p = Pattern.compile("(\\d{4}-\\d{2}-\\d{2})");
		Matcher matcher = p.matcher("");
		String dateString = "";
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		for (String line : execResult) {
			if (line.contains("@@")) {
				// add changeset
				this.changeSetVersions.add(line.trim());
			} else {
				// get CR date
				matcher.reset(line);
				while (matcher.find()) {
					dateString = matcher.replaceAll("$1");
					try {
						this.crDate = format.parse(dateString);
						break;
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	public List<String> getChangeSetVersions() {
		return this.changeSetVersions;
	}

	public Date getCRDate() {
		return this.crDate;
	}
}
